#include "Parser.h"

Parser::Parser(QVector<QString> * tokens) : _tokens(tokens){
    _gramma = new GrammarChecker();
    _retTokens = new QVector<QString>();
}

QVector<QString> * Parser::getTokens() const{
    return _retTokens;
}

void Parser::showErr(int * line,QString * ErrCont, int type){
    switch (type) {
    case 0:
            Console::print(Communicate::ERR_LINE+QString::number(*line)+(*ErrCont));
        break;
    default:
            Console::print(Communicate::WAR_LINE+QString::number(*line)+(*ErrCont));
        break;
    }
}

void Parser::showErr(int * lineBegin,int * lineEnd,QString * ErrCont){
    Console::print(Communicate::ERR_GAME+QString::number(*lineBegin)+"-"+QString::number(*lineEnd)+(*ErrCont));
}

bool Parser::parse() {

    QString listOfTok =  "nP,cL,wN,nK,br,nR,r1,r2,k1,k2,w";
    _gramma->addToken(&listOfTok);
    _gramma->addMoveRule("br->nP|br|nR");
    _gramma->addMoveRule("nP->cL");
    _gramma->addMoveRule("cL->wN");
    _gramma->addMoveRule("wN->nK");
    _gramma->addMoveRule("nK->br");
    _gramma->addMoveRule("nR->r1");
    _gramma->addMoveRule("r1->r2|k1|w");
    _gramma->addMoveRule("r2->k2|nR|br|w");
    _gramma->addMoveRule("k1->r2|w");
    _gramma->addMoveRule("k2->w|br|nR");
    _gramma->addMoveRule("w->br");
    QString tokPrev,tokPrevV,token;
    QString tokenType="";
    QString tokenVal="";
    QChar znak;
    QVector<QString> tokenTypes;
    QVector<QString> tokenValues;

    bool moveFirst=true;
    bool commFirst=true;
    bool tokenTypeRead=true;
    bool errorPrinted=false;
    bool correct=true;
    int lines=1;
    for (int a=0;a<_tokens->length();a++){
        token = _tokens->at(a);
        for (int b=0;b<token.length();b++){
            znak = token[b];
            if (tokenTypeRead){
                if (znak==','){
                    tokenTypeRead=false;
                }else
                    tokenType+=znak;
            }else{
                tokenVal+=znak;
            }
        }
        if (tokenType.compare("br")==0){
            lines++;
            errorPrinted=false;
        }else if (tokenType.compare("r")==0){
            if (moveFirst){
                tokenType="r1";
                moveFirst=false;
                commFirst=true;
            }else{
                tokenType="r2";
                moveFirst=true;
                commFirst=false;
            }
        }else if (tokenType.compare("k")==0){
            if (commFirst){
                tokenType="k1";
            }else{
                tokenType="k2";
            }
        }else if (tokenType.compare("w")==0)
            moveFirst=true;
        if (a==0){
            tokPrev=tokenType;
            tokPrevV=tokenVal;
        }else{
            if (!(_gramma->checkMove(&tokPrev,&tokenType))){
                if (!errorPrinted){
                    QString error = Communicate::ERR_LINE+QString::number(lines)+": Nieoczekiwany token \""+tokenVal+"\"["+tokenType+"] po \""+tokPrevV+"\"["+tokPrev+"].";
                    Console::print(error);
                }
                errorPrinted=true;
                moveFirst=true;
                correct=false;
            }
        }
        tokenTypeRead = true;
        tokPrevV=tokenVal;
        tokPrev=tokenType;
        tokenTypes.append(tokenType);
        tokenValues.append(tokenVal);
        tokenVal="";
        tokenType="";
    }

    //kolejny etap parsowania
    //wydzielanie gier
    if (correct){

        QVector<ParserGameInfoChecker*> gameInfoCheckerVec;
        gameInfoCheckerVec.append(new  ParserGameInfoChecker());
        QVector<QString> tokenHeaders;
        QVector<QString> tokenMoves;
        lines=1;
        correct=true;
        int gameBegin=0;
        int status = 2; //0-header, 1-moves, 2-break
        int indexMove=0;
        bool gameCorrect = true;
        int gameReaded=0;
        int gameError=0;
        bool moveExcepted=false; //0-header, 1-moves
        bool numErrorDisp=false;
        for (int a=0;a<tokenTypes.length();a++){
            tokenType = tokenTypes.at(a);
            tokenVal = tokenValues.at(a);
            if (tokenType.compare("br")==0){
                if(a>0 && tokenTypes.at(a-1).compare("br")==0){//pusta linia oddzielajaca
                    if (status!=2)
                        status=2;
                }
                lines++;
                numErrorDisp=false;
            }else if (tokenType.compare("cL")==0){ //typ naglowka
                if (status==2){
                    if (moveExcepted){
                        showErr(& lines,& Communicate::NO_MOVES);
                        tokenHeaders.clear();
                        gameCorrect=true;
                        moveExcepted=false;
                        gameError++;
                    }
                    gameBegin=lines;
                    indexMove=0;
                    tokenMoves.clear();
                }else if (status==1){
                    showErr(& lines,& Communicate::HEAD_NOT_MOVE,1);
                }
                moveExcepted=true;
                status=0;
                if (a+1<tokenTypes.length()){
                    a++;
                    int val = gameInfoCheckerVec.last()->addHeader(tokenVal,tokenValues.at(a));
                    if (val>0){
                        gameCorrect=false;
                        switch (val) {
                        case -1:
                            showErr(& lines,& Communicate::HEAD_ERR_EMPTY);
                            break;
                        case 1:
                            showErr(& lines,& Communicate::HEAD_REPEAT);
                            break;
                        case 2:
                            showErr(& lines,& Communicate::HEAD_ERR_SITE,1);
                            gameCorrect=true;
                            break;
                        case 3:
                            showErr(& lines,& Communicate::HEAD_ERR_DATA);
                            break;
                        case 4:
                            showErr(& lines,& Communicate::HEAD_ERR_ROUND);
                            break;
                        case 5:
                            showErr(& lines,& Communicate::HEAD_ERR_RESULT);
                            break;
                        case 6:
                            showErr(& lines,& Communicate::HEAD_ERR_ECO);
                            break;
                        case 9:
                            showErr(& lines,& Communicate::HEAD_ERR_UNKNOWN,1);
                            gameCorrect=true;
                            break;
                        default:
                            showErr(& lines,& Communicate::HEAD_ERR_SUP);
                            break;
                        }
                    }else{
                        tokenHeaders.append("nP");
                        tokenHeaders.append(tokenType+','+tokenVal);
                        tokenHeaders.append("wN,"+tokenValues.at(a));
                        tokenHeaders.append("nK");
                        tokenHeaders.append("br");
                    }
                }else{
                    showErr(& lines,& Communicate::PARSE_NO_HEAD_VAL);
                    gameCorrect=false;
                }
            }else if (tokenType.compare("nR")==0){ //numer ruchu
                if (status==0){
                    showErr(& lines,& Communicate::MOV_NOT_HEAD,1);
                    status=1;
                }else if (status==2){
                    if (!moveExcepted){
                        showErr(& lines,& Communicate::NO_HEAD);
                        tokenHeaders.clear();
                        tokenMoves.clear();
                        gameCorrect=true;
                        moveExcepted=false;
                        indexMove=0;
                        gameError++;
                    }
                    status=1;
                }
                indexMove++;
                if (tokenVal.toInt()!=indexMove && !numErrorDisp){
                    showErr(& lines,& Communicate::MOVES_ERR_NUMS);
                    numErrorDisp=true;
                    gameCorrect=false;
                }
            }else if (tokenType.compare("r1")==0){
                if (a+1<tokenTypes.length() && tokenTypes.at(a+1).compare("k1")==0){
                    a++;
                    tokenMoves.append(tokenType+','+tokenVal);
                    tokenMoves.append("k1,"+tokenValues.at(a));
                }else{
                    tokenMoves.append(tokenType+','+tokenVal);
                }
            }else if (tokenType.compare("r2")==0){
                if (a+1<tokenTypes.length() && tokenTypes.at(a+1).compare("k2")==0){
                    a++;
                    tokenMoves.append(tokenType+','+tokenVal);
                    tokenMoves.append("k2,"+tokenValues.at(a));
                }else{
                    tokenMoves.append(tokenType+','+tokenVal);
                }
            }else if (tokenType.compare("w")==0){
                if (gameCorrect){
                    if (gameInfoCheckerVec.last()->checkCorrect()){
                        tokenMoves.append(tokenType+','+tokenVal);
                        foreach (QString tok, tokenHeaders) {
                            _retTokens->append(tok);
                        }
                        _retTokens->append("br");
                        foreach (QString tok, tokenMoves) {
                            _retTokens->append(tok);
                        }
                        _retTokens->append("br");
                        gameReaded++;
                    }else{
                        showErr(& gameBegin,& lines,& Communicate::HEAD_NOT_ALL);
                        gameError++;
                    }
                }else
                    gameError++;
                gameInfoCheckerVec.append(new  ParserGameInfoChecker());
                tokenHeaders.clear();
                tokenMoves.clear();
                gameCorrect=true;
                moveExcepted=false;
                indexMove=0;
            }
        }
        if (gameInfoCheckerVec.last()->checkCorrect() ||tokenMoves.length()>0){
            gameError++;
            showErr(& lines,& Communicate::PARSE_ERR_EOF);
        }
        Console::print("Sparsowano poprawnie "+ QString::number(gameReaded)+" z "+ QString::number(gameReaded+gameError)+" gier.");
        if (gameReaded==0)
            correct = false;

        foreach (ParserGameInfoChecker * gameInfo, gameInfoCheckerVec) {
            delete gameInfo;
        }
    }
    if (correct)
        Console::print(Communicate::PARSE_OK);
    return correct;
}

Parser::~Parser() {
    delete _retTokens;
    delete _gramma;
}
